use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};

use crate::{component::{ComponentId, ComponentChannelVecTrait, ComponentTrait, get_component_id}, entity::Entity, MangoEcsError};


///
/// [`ArchetypeChannel`] 是一批相同组件的集合
/// 
/// [`ArchetypeChannel::component_id`] 是存储的组件的类型
/// 
/// [`ArchetypeChannel::data`] 是存储组件的容器,其内部是 Vec<T>
/// 
/// 
pub(crate) struct ArchetypeChannel {
    pub(crate) component_id:ComponentId,
    pub(crate) data:Box<dyn ComponentChannelVecTrait>
}

impl ArchetypeChannel {
    pub(crate) fn new<C:ComponentTrait>() -> Self {
        ArchetypeChannel {
            component_id: get_component_id::<C>(),
            data: Box::new(RwLock::new(Vec::<C>::with_capacity(1))) as Box<dyn ComponentChannelVecTrait>,
        }
    }

    pub(crate) fn new_same_type(&self) -> Self {
        Self {
            component_id:self.component_id,
            data:self.data.new_same_type()
        }
    }

    pub(crate) fn as_mut_vec<T:'static>(&mut self) -> &mut [T] {
        self.data
            .as_any_mut()
            .downcast_mut::<RwLock<Vec<T>>>()
            .unwrap()
            .get_mut()
            .unwrap()
    }
}


///
/// [`Archetype`] 是具有相同类型组件的 [`Entity`] 集合
/// 
/// [`Entity`] 在 [`Archetype::entities`] 中的索引同时也是其组件在 [`ArchetypeChannel::data`] 中的位置索引
/// 
/// [`Archetype::index_in_world`] 在 [`World::archetypes`] 中的索引位置
/// 
/// 
pub struct Archetype {
    /// 具有相同类型组件的 [`Entity`] 集合
    pub(crate) entities:Vec<Entity>,
    /// 该 Archetype 所具有的 [`Component`] 类型的集合
    pub(crate) channels:Vec<ArchetypeChannel>,
    /// 该 Archetype 在 [`World`] 中的索引位置
    pub(crate) index_in_world:usize
}

impl Archetype {
    pub(crate) fn new(index_in_world:usize) -> Self {
        Self {
            entities:vec![],
            channels:vec![],
            index_in_world
        }
    }

    pub(crate) fn get_read_channel<T:'static>(&self,channel_index:usize) -> Result<RwLockReadGuard<Vec<T>>,MangoEcsError> {
        self.channels[channel_index]
            .data
            .as_any()
            .downcast_ref::<RwLock<Vec<T>>>()
            .unwrap()
            .try_read()
            .map_err(|_|MangoEcsError::ChannelExclusivelyLocked)
    } 


    pub(crate) fn get_write_channel<T:'static>(&self,channel_index:usize) -> Result<RwLockWriteGuard<Vec<T>>,MangoEcsError> {
        self.channels[channel_index]
            .data
            .as_any()
            .downcast_ref::<RwLock<Vec<T>>>()
            .unwrap()
            .try_write()
            .map_err(|_|MangoEcsError::ChannelExclusivelyLocked)
    }

}


